package cn.itcast.concurrent.disruptor;

import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;

import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/***
 *
 * BlockingWaitStrategy
 * 默认等待策略，这个策略的内部适用一个锁和条件变量来控制线程的执行和等待（Java基本的同步方法）。
 * BlockingWaitStrategy是最慢的等待策略，但也是CPU使用率最低和最稳定的选项。
 * 然而，可以根据不同的部署环境调整选项以提高性能。
 *
 * SleepingWaitStrategy
 * 和BlockingWaitStrategy一样，SpleepingWaitStrategy的CPU使用率也比较低。
 * 它的方式是循环等待并且在循环中间调用LockSupport.parkNanos(1)来睡眠，
 * （在Linux系统上面睡眠时间60µs）.然而，它的优点在于生产线程只需要计数，而不执行任何指令。
 * 并且没有条件变量的消耗。但是，事件对象从生产者到消费者传递的延迟变大了。
 * SleepingWaitStrategy最好用在不需要低延迟，而且事件发布对于生产者的影响比较小的情况下。比如异步日志功能。
 *
 * YieldingWaitStrategy
 * YieldingWaitStrategy是可以被用在低延迟系统中的两个策略之一，
 * 这种策略在减低系统延迟的同时也会增加CPU运算量。
 * YieldingWaitStrategy策略会循环等待sequence增加到合适的值。
 * 循环中调用Thread.yield()允许其他准备好的线程执行。
 * 如果需要高性能而且事件消费者线程比逻辑内核少的时候，推荐使用YieldingWaitStrategy策略。
 * 例如：在开启超线程的时候。
 *
 * BusySpinW4aitStrategy
 * BusySpinWaitStrategy是性能最高的等待策略，同时也是对部署环境要求最高的策略。
 * 这个性能最好用在事件处理线程比物理内核数目还要小的时候。例如：在禁用超线程技术的时候。
 */
public class LongEventWithMethodRef {
    public static void handleEvent(LongEvent event, long sequence, boolean endOfBatch) {
        System.out.println(event.getValue());
    }

    public static void translate(LongEvent event, long sequence, ByteBuffer buffer) {
        event.setValue(buffer.getLong(0));
    }

    public static void main(String[] args) throws Exception {
        // Executor that will be used to construct new threads for consumers 
        Executor executor = Executors.newCachedThreadPool();
        // Specify the size of the ring buffer, must be power of 2.
        int bufferSize = 1024;
        // Construct the Disruptor 
        Disruptor<LongEvent> disruptor = new Disruptor<>(LongEvent::new, bufferSize, executor);
        // Connect the handler 
        disruptor.handleEventsWith(LongEventWithMethodRef::handleEvent);
        // Start the Disruptor, starts all threads running 
        disruptor.start();
        // Get the ring buffer from the Disruptor to be used for publishing. 
        RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();

        LongEventProducer producer = new LongEventProducer(ringBuffer);

        ByteBuffer bb = ByteBuffer.allocate(8);
        for (long l = 0; true; l++) {
            bb.putLong(0, l);
            ringBuffer.publishEvent(LongEventWithMethodRef::translate, bb);
            Thread.sleep(1000);
        }
    }
} 